package com.aoyy.dao;

import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.lang.reflect.ParameterizedType;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Query;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate4.HibernateTemplate;
import org.springframework.util.Assert;
import com.aoyy.util.Page;
/**======================================================================================
 * Base DAO class <code>BaseDao<T></code> 
 * @author this author
 * @date 2017-08-05
 * @param <T>
 * ======================================================================================== */
public class BaseDao<T> {
	private Class<T> entityClass;
	@Autowired
	private SessionFactory sessionFactory;
	@Autowired
	private HibernateTemplate hibernateTemplate;
	
	@SuppressWarnings("unchecked")
	public BaseDao() {
		Type type = getClass().getGenericSuperclass();
		Type[] parameters = ((ParameterizedType) type).getActualTypeArguments();
		entityClass = (Class<T>) parameters[0];
	}
	
	protected void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}
	
	protected SessionFactory getSessionFactory() {
		return sessionFactory;
	}
	
	protected Session getSession() {
		return sessionFactory.getCurrentSession();
	}
	
	protected HibernateTemplate getHibernateTemplate() {
		return hibernateTemplate;
	}
    
	public void initialize(Object entity) {
		getHibernateTemplate().initialize(entity);
	}
	
	public T load(Serializable id) {
		return (T) getHibernateTemplate().load(entityClass, id);
	}
	
	public T get(Serializable id) {
		return (T) getHibernateTemplate().get(entityClass, id);
	}
	
	public List<T> loadAll() {
		return getHibernateTemplate().loadAll(entityClass);
	}
	
	public void save(T entity) {
		getHibernateTemplate().save(entity);
	}
	
	public void delete(T entity) {
		getHibernateTemplate().delete(entity);
	}
	
	public void update(T entity) {
		getHibernateTemplate().update(entity);
	}
	
	@SuppressWarnings("rawtypes")
	public List find(String hql) {
		return getHibernateTemplate().find(hql);
	}
	
	@SuppressWarnings("rawtypes")
	public List find(String hql, Object... values) {
		return getHibernateTemplate().find(hql, values);
	}
	
	public void executeUpdate(String hql) {
		Query query = createQuery(hql);
		query.executeUpdate();
	}
	
	public void executeUpdate(String hql, Object... values) {
		Query query = createQuery(hql, values);
		query.executeUpdate();
	}
	
	public Query createQuery(String hql) {
		Assert.hasText(hql);
		Query query = getSession().createQuery(hql);
		return query;
	}

	public Query createQuery(String hql, Object... values) {
		Assert.hasText(hql);
		Query query = getSession().createQuery(hql);
		if (values != null) {
			int length = values.length;
			for (int i = 0; i < length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		return query;
	}
	
	// Paged Query
	public Page pagedQuery(String hql, int pageNo, int pageSize, Object... values) {
		Assert.hasText(hql);
		Assert.isTrue(pageNo >= 1, "Pagination is required to start from 1.");
		String queryString = "select count(*) " + removeSelect(removeOrderBy(hql));
		@SuppressWarnings("rawtypes")
		List list = getHibernateTemplate().find(queryString, values);
		int recordCount = ((Long)list.get(0)).intValue();
		if (recordCount < 1) {
			return new Page();
		}
		int firstResult = Page.getFirstResult(pageNo, pageSize);
		Query query = createQuery(hql, values);
		list = query.setFirstResult(firstResult).setMaxResults(pageSize).list();
		return new Page(firstResult, recordCount, pageSize, list);
	}
	
	private static String removeSelect(String hql) {
		Assert.hasText(hql);
		int index = hql.toLowerCase().indexOf("from");
		Assert.isTrue(index != -1, "HQL: " + hql + " contains 'from'.");
		return hql.substring(index);
	}
	
	private static String removeOrderBy(String hql) {
		Assert.hasText(hql);
		Pattern pattern = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*", Pattern.CASE_INSENSITIVE);
		Matcher matcher = pattern.matcher(hql);
		StringBuffer sb = new StringBuffer();
		while(matcher.find()) {
			matcher.appendReplacement(sb, "");
		}
		matcher.appendTail(sb);
		return sb.toString();
	}

}
